﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Text;
using System.Threading;
using RabbitMQ.Client;

namespace PoshRabbit
{
    public class RabbitConnector
    {
        private readonly IRabbitMQConnection _connectionSettings;
        private IConnection _connection;
        private IModel _model;

        public RabbitConnector( IRabbitMQConnection connectionSettings )
        {
            _connectionSettings = connectionSettings;
        }

        ConnectionFactory GetFactory()
        {
            ConnectionFactory factory = new ConnectionFactory
            {
                HostName = HostName
            };

            if (!String.IsNullOrEmpty(Protocol))
            {
                var type = typeof(Protocols);
                var protocolProperty = (from prop in type.GetProperties(BindingFlags.Public | BindingFlags.Static)
                                        where StringComparer.InvariantCultureIgnoreCase.Equals(Protocol, prop.Name)
                                        select prop).FirstOrDefault();

                factory.Protocol = protocolProperty.GetValue(null, null) as IProtocol;
            }

            if (!String.IsNullOrEmpty(UserName))
            {
                factory.UserName = UserName;
            }
            if (null != Password)
            {
                factory.Password = Password.ConvertToUnsecureString();
            }
            if (!String.IsNullOrEmpty(VirtualHost))
            {
                factory.VirtualHost = VirtualHost;
            }
            return factory;
        }

        public void CreateModel()
        {
            ConnectionFactory factory = GetFactory();

            _connection = factory.CreateConnection();

            _model = Connection.CreateModel();

            Model.ExchangeDeclare(Exchange, ExchangeType.ToString());
        }

        public void DestroyModel()
        {
            var model = Interlocked.Exchange(ref _model, null);
            var connection = Interlocked.Exchange(ref _connection, null);

            if (null != model && model.IsOpen )
            {
                try
                {
                    model.Close();
                    model.Dispose();
                }
                catch
                {
                }
            }

            if (null != connection && connection.IsOpen )
            {
                try
                {
                    connection.Close();
                    connection.Dispose();
                }
                catch
                {

                }
            }
        }

        internal string Exchange
        {
            get { return _connectionSettings.Exchange; }
            set { _connectionSettings.Exchange = value; }
        }

        internal string ExchangeType
        {
            get { return _connectionSettings.ExchangeType; }
            set { _connectionSettings.ExchangeType = value; }
        }

        internal string RoutingKey
        {
            get { return _connectionSettings.RoutingKey; }
            set { _connectionSettings.RoutingKey = value; }
        }

        internal string VirtualHost
        {
            get { return _connectionSettings.VirtualHost; }
            set { _connectionSettings.VirtualHost = value; }
        }

        internal string HostName
        {
            get { return _connectionSettings.HostName; }
            set { _connectionSettings.HostName = value; }
        }

        internal string UserName
        {
            get { return _connectionSettings.UserName; }
            set { _connectionSettings.UserName = value; }
        }

        internal SecureString Password
        {
            get { return _connectionSettings.Password; }
            set { _connectionSettings.Password = value; }
        }

        internal string Protocol
        {
            get { return _connectionSettings.Protocol; }
            set { _connectionSettings.Protocol = value; }
        }

        internal string Encoding
        {
            get { return _connectionSettings.Encoding; }
            set { _connectionSettings.Encoding = value; }
        }

        internal IModel Model
        {
            get { return _model; }
        }

        internal IConnection Connection
        {
            get { return _connection; }
        }

        internal Encoding MessageEncoding
        {
            get
            {
                if( String.IsNullOrEmpty( Encoding))
                {
                    return null;
                }

                var t = typeof(Encoding);
                var prop = (from p in t.GetProperties(BindingFlags.Static | BindingFlags.Public)
                            where StringComparer.InvariantCultureIgnoreCase.Equals(Encoding, p.Name)
                            select p).FirstOrDefault();

                if (null == prop)
                {
                    return null;
                }

                return prop.GetValue(null, null) as System.Text.Encoding;
            }
        }
    }
}
